package com.cybertron.am.wordpress.util;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cybertron.am.wordpress.Constants;
import com.cybertron.am.wordpress.domain.*;
import com.cybertron.am.wordpress.domain.woo.WooCategory;
import com.cybertron.am.wordpress.domain.woo.WooProduct;
import com.cybertron.am.wordpress.domain.woo.WooVariant;
import com.cybertron.am.wordpress.persistence.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.jayway.jsonpath.TypeRef;
import com.ne.boot.common.exception.NEException;
import com.ne.boot.common.util.JsonUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
public class WooUtil {
    @Autowired
    private CrawlSeedMapper seedMapper;
    @Autowired
    private CrawlCategoryMapper categoryMapper;
    @Autowired
    private CrawlProductMapper productMapper;
    @Autowired
    private WebsiteMapper websiteMapper;
    @Autowired
    private WooCategoryRelMapper categoryRelMapper;
    @Autowired
    private WooProductRelMapper productRelMapper;

    public void pushData(String seedId, String seedCategoryId, String websiteId) {
        List<String> seedCategoryIds = pushCategory(seedId, seedCategoryId, websiteId);
        if (CollectionUtils.isEmpty(seedCategoryIds)) {
            log.info("无子级分类");
            return;
        }
        pushProduct(seedId, seedCategoryIds, websiteId);
    }

    public List<String> pushCategory(String seedId, String seedCategoryId, String websiteId) {
        categoryRelMapper.delete(new LambdaQueryWrapper<WooCategoryRel>()
                .eq(WooCategoryRel::getWebsiteId, websiteId)
        );
        Website website = websiteMapper.selectById(websiteId);
        CrawlSeed seed = seedMapper.selectById(seedId);
        List<CrawlCategory> categories = categoryMapper.selectList(new LambdaQueryWrapper<CrawlCategory>()
                .eq(CrawlCategory::getSeedId, seed.getId())
                .eq(StringUtils.isNotBlank(seedCategoryId), CrawlCategory::getParentId, seedCategoryId)
                .isNull(StringUtils.isBlank(seedCategoryId), CrawlCategory::getParentId)
                .last("ORDER BY rand() LIMIT 5")
        );
        if (CollectionUtils.isEmpty(categories)) {
            return null;
        }
        List<String> ids = categories.stream()
                .map(CrawlCategory::getId)
                .collect(Collectors.toList());
        pushCategory(seed, website, categories, ids);
        return ids;
    }

    private void pushCategory(CrawlSeed seed, Website website, List<CrawlCategory> categories, List<String> ids) {
        for (CrawlCategory category : categories) {
            pushCategory(seed, website, category);
            List<CrawlCategory> childs = categoryMapper.selectList(new LambdaQueryWrapper<CrawlCategory>()
                    .eq(CrawlCategory::getParentId, category.getId())
            );
            if (CollectionUtils.isNotEmpty(childs)) {
                ids.addAll(childs.stream()
                        .map(CrawlCategory::getId)
                        .collect(Collectors.toList())
                );
                pushCategory(seed, website, childs, ids);
            }
        }
    }

    private void pushCategory(CrawlSeed seed, Website website, CrawlCategory category) {
        WooCategoryRel categoryRel = categoryRelMapper.selectOne(new LambdaQueryWrapper<WooCategoryRel>()
                .eq(WooCategoryRel::getWebsiteId, website.getId())
                .eq(WooCategoryRel::getCategoryId, category.getId())
                .last("LIMIT 1")
        );
        if (categoryRel != null) {
            return;
        }
        CrawlCategory parent = StringUtils.isNotBlank(category.getParentId())
                ? categoryMapper.selectById(category.getParentId()) : null;
        WooCategoryRel parentRel = parent == null ? null : categoryRelMapper.selectOne(new LambdaQueryWrapper<WooCategoryRel>()
                .eq(WooCategoryRel::getWebsiteId, seed.getId())
                .eq(WooCategoryRel::getCategoryId, parent.getId())
                .last("LIMIT 1")
        );
        String url = "https://" + website.getDomain() + Constants.Woocommerce.WOO_CATEGORY_URL;
        String body = JsonUtil.writeValueAsString(WooCategory.builder()
                .name(category.getTitle())
                .parent(parentRel != null ? parentRel.getWooId() : null)
                .build());
        HttpUtil.ExecutorBuilder builder = HttpUtil.builder()
                .url(url)
                .headers(WooUtil.buildHeaders())
                .parser((response, content) -> {
                    log.info("添加分类响应{}", content);
                    return JsonUtil.<WooCategory>readValue(content, new TypeReference<WooCategory>() {
                    });
                });
        WooCategory wooCategory = builder.post(HttpUtil.MediaType.JSON, body);
        categoryRelMapper.insert(WooCategoryRel.builder()
                .id(SnowFlake.nextIdStr())
                .categoryId(category.getId())
                .websiteId(website.getId())
                .wooId(wooCategory.getId())
                .build());
    }

    @SneakyThrows
    public void pushProduct(String seedId, List<String> seedCategoryIds, String websiteId) {
        productRelMapper.delete(new LambdaQueryWrapper<WooProductRel>()
                .eq(WooProductRel::getWebsiteId, websiteId)
        );
        Website website = websiteMapper.selectById(websiteId);
        CrawlSeed seed = seedMapper.selectById(seedId);
        List<CrawlProduct> products = productMapper.selectList(new LambdaQueryWrapper<CrawlProduct>()
                .eq(CrawlProduct::getSeedId, seed.getId())
                .in(CollectionUtils.isNotEmpty(seedCategoryIds), CrawlProduct::getCategoryId, seedCategoryIds)
                .last("ORDER BY rand() LIMIT 100")
        );
        Constants.POOL.submit(() ->
                products.parallelStream().forEach(product -> {
                    try {
                        pushProduct(website, product);
                    } catch (Throwable e) {
                        log.error("添加商品失败", e);
                    }
                })).get();
    }

    public synchronized void pushProduct(Website website, CrawlProduct product) {
        WooProductRel productRel = productRelMapper.selectOne(new LambdaQueryWrapper<WooProductRel>()
                .eq(WooProductRel::getWebsiteId, website.getId())
                .eq(WooProductRel::getProductId, product.getId())
                .last("LIMIT 1")
        );
        if (productRel != null) {
            return;
        }
        List<CrawlProduct.CrawlVariant> variants = ArrayUtils.isNotEmpty(product.getVariants()) ? Arrays.stream(product.getVariants())
                .limit(6)
                .collect(Collectors.toList()) : null;
        CrawlCategory category = categoryMapper.selectById(product.getCategoryId());
        WooProduct wooProductParam = buildProduct(website, category, product, variants);
        String body = JsonUtil.writeValueAsString(wooProductParam);
        String url = "https://" + website.getDomain() + Constants.Woocommerce.WOO_PRODUCT_URL;
        log.info("woo添加商品请求 {},{}", url, body);
        HttpUtil.ExecutorBuilder builder = HttpUtil.builder()
                .url(url)
                .headers(WooUtil.buildHeaders())
                .parser((response, content) -> {
                    log.info("添加商品响应{}", content);
                    String id = JsonUtil.readValue(content, "$.id", new TypeRef<String>() {
                    });
                    if (StringUtils.isBlank(id)) {
                        throw new NEException("添加商品失败");
                    }
                    return JsonUtil.readValue(content, new TypeReference<WooProduct>() {
                    });
                });
        WooProduct wooProduct = builder.post(HttpUtil.MediaType.JSON, body);
        productRelMapper.insert(WooProductRel.builder()
                .id(SnowFlake.nextIdStr())
                .productId(product.getId())
                .websiteId(website.getId())
                .wooId(wooProduct.getId())
                .build());
        if (CollectionUtils.isNotEmpty(variants)) {
            pushVariant(wooProduct.getId(), website, product, variants);
        }
    }

    public WooProduct buildProduct(Website website, CrawlCategory category,
                                   CrawlProduct product, List<CrawlProduct.CrawlVariant> variants) {
        WooProduct wooProduct = new WooProduct();
        wooProduct.setName(product.getTitle());
        // 生成attrs
        if (ArrayUtils.isNotEmpty(product.getOptions())) {
            List<String> options = Arrays.asList(product.getOptions());
            if (options.size() == 1) {
                options = new ImmutableList.Builder<String>()
                        .add("Color")
                        .add("Size")
                        .build();
                product.setOptions(options.toArray(new String[]{}));
            }
            wooProduct.setType("variable");
            Map<Integer, Set<String>> map = new HashMap<>();
            for (CrawlProduct.CrawlVariant variant : variants) {
                for (int i = 0; i < variant.getOptions().length; i++) {
                    Set<String> values = MapUtils.getObject(map, i, new HashSet<>());
                    values.add(variant.getOptions()[i]);
                    map.put(i, values);
                }
            }
            List<WooProduct.Attribute> attrs = new ArrayList<>();
            for (int i = 0; i < options.size(); i++) {
                String option = options.get(i);
                attrs.add(WooProduct.Attribute.builder()
                        .name(option)
                        .options(MapUtils.getObject(map, i))
                        .build());
            }
            wooProduct.setAttributes(attrs);

            List<WooProduct.DefaultAttribute> defaultAttrs = new ArrayList<>();
            for (WooProduct.Attribute attr : attrs) {
                String option = CollectionUtils.isNotEmpty(attr.getOptions()) ? CollUtil.getFirst(attr.getOptions()) : null;
                if (StringUtils.isBlank(option)) {
                    continue;
                }
                defaultAttrs.add(WooProduct.DefaultAttribute.builder()
                        .name(attr.getName())
                        .option(option)
                        .build());
            }
            wooProduct.setDefaultAttributes(defaultAttrs);
        }
        String[] images = product.getImages();
        if (ArrayUtils.isNotEmpty(images)) {
            List<WooProduct.Image> wooImages = Arrays.stream(images)
                    .map(v ->
                            WooProduct.Image.builder()
                                    .src(v)
                                    .build()
                    )
                    .collect(Collectors.toList());
            wooProduct.setImages(wooImages);
        }
        wooProduct.setRegularPrice(product.getPrice());
        wooProduct.setDescription(product.getDescription());
        wooProduct.setShortDescription(product.getTitle());
        WooCategoryRel categoryRel = category == null ? null : categoryRelMapper.selectOne(new LambdaQueryWrapper<WooCategoryRel>()
                .eq(WooCategoryRel::getWebsiteId, website.getId())
                .eq(WooCategoryRel::getCategoryId, product.getCategoryId())
                .last("LIMIT 1")
        );
        if (categoryRel != null) {
            wooProduct.setCategories(Collections.singletonList(
                    WooCategory.builder()
                            .id(categoryRel.getWooId())
                            .build())
            );
        }
        return wooProduct;
    }

    public synchronized void pushVariant(String wooProductId, Website website, CrawlProduct product, List<CrawlProduct.CrawlVariant> variants) {
        List<WooVariant> wooVariantParam = buildVariants(product, variants);
        if (CollectionUtils.isEmpty(wooVariantParam)) {
            return;
        }
        String body = JsonUtil.writeValueAsString(new ImmutableMap.Builder<String, List<WooVariant>>()
                .put("create", wooVariantParam)
                .build());
        String url = "https://" + String.format(website.getDomain() + Constants.Woocommerce.WOO_VARIANT_URL + "/batch", wooProductId);
        log.info("woo添加变种请求{},{}", url, body);
        List<WooVariant> wooVariants = HttpUtil.post(url, body, HttpUtil.MediaType.JSON, WooUtil.buildHeaders(), (response, content) -> {
            log.info("woo添加变种响应{},{},{}", url, body, content);
            return JsonUtil.readValue(content, "$.create", new TypeRef<List<WooVariant>>() {
            });
        });
        log.info("添加变种响应{}", wooVariants.size());
    }

    private List<WooVariant> buildVariants(CrawlProduct product, List<CrawlProduct.CrawlVariant> variants) {
        if (ArrayUtils.isEmpty(product.getVariants())) {
            return null;
        }
        if (product.getVariants().length == 1 && StringUtils.equalsIgnoreCase(product.getVariants()[0].getTitle(), "Default Title")) {
            return null;
        }
        String randomPrice = String.valueOf(RandomUtils.nextInt(40, 100));
        String[] options = product.getOptions();
        return variants.stream()
                .limit(5)
                .map(variant -> {
                    List<WooVariant.Attribute> attrs = new ArrayList<>();
                    for (int i = 0; i < variant.getOptions().length; i++) {
                        String option = variant.getOptions()[i];
                        WooVariant.Attribute attribute = WooVariant.Attribute.builder()
                                .name(options[i])
                                .option(option)
                                .build();
                        attrs.add(attribute);
                    }
                    String price = StringUtils.isNotBlank(variant.getPrice()) ? variant.getPrice() : product.getPrice();
                    if (StringUtils.isBlank(price)) {
                        price = randomPrice;
                    }
                    return WooVariant.builder()
                            .regularPrice(price)
                            .image(WooProduct.Image.builder()
                                    .src(variant.getImage())
                                    .build())
                            .attributes(attrs)
                            .stockQuantity(130)
                            .build();
                })
                .collect(Collectors.toList());
    }

    public static Map<String, String> buildHeaders() {
        Map<String, String> headers = new HashMap<>();
        String token = String.format("%s:%s", Constants.Woocommerce.WOO_CONSUMER_KEY, Constants.Woocommerce.WOO_CONSUMER_SECRET);
        String encode = Base64.getEncoder().encodeToString(token.getBytes());
        headers.put("Authorization", "Basic " + encode);
        return headers;
    }


}